/*
 * Università degli Studi di Trento
 * Facoltà di Scienze MM.FF.NN.
 * Corso di Laurea Triennale in Informatica
 * A.A. 2011/2012
 * Progetto per il corso di Sistemi Operativi 1
 * Progetto 3: 
 * Autori:
 * Bertoldi Lorenzo 155863
 * Cattin Cosso Manuel 146607
 * Parrello Nicola 145610
 */

/********************************************************************************
  Questa file contiene l'implementazione dei metodi del threadPadre.
********************************************************************************/


#include "threadFiglio.h"
#include "threadPadre.h"
#include "listaThread.h"
#include "definizioni.h"

#include <signal.h>


/*
 * costruttore:
 * Alloca lo spazio per la struttura
 * Se non esiste crea la directory per i file di log
 * Ricrea il file di log con nome cmd.log
 * Ridirige l'output del programma
 * Restituisce il puntatore
*/
pThreadPadre ThreadPadre_create (){
    pThreadPadre tp=new(ThreadPadre);
    tp->listaFigli=ListaThread_create();
    //folderExist();
    if(mkdir(LOGDIRECTORY,O_RDWR | S_IRWXU |S_IRWXG |S_IRWXO)==-1){
        /*
         * Se la cartella esiste gia' da errore ma non serve uscire.
         */
        //perror("Impossibile creare la directory: "LOGDIRECTORY);
    }
    
    /*
     * Creo il file di log del padre
     */
    char* mode="w";
    tp->file=fopen(LOGDIRECTORY"/cmd.log", mode);
    if(tp->file==NULL){
        perror("Errore creazione file padre: "LOGDIRECTORY"cmd.log");
    }else{
        //Assegno da subito i privilegi per il file
        chmod(LOGDIRECTORY"/cmd.log",O_RDWR | S_IRWXU |S_IRWXG |S_IRWXO);
        //Ridirigo l'output del padre in nel file di log
        //dup2(tp->file, STDOUT_FILENO);
    }
    return tp;
}


/*
 * ascoltaEScrivi:
 * Ascolta la pipe in modo da sentire cosa gli comunicano i figli
 * e scrive cio che ha letto
*/
void ThreadPadre_ascoltaEScrivi (pThreadPadre tp){
  int BUFSIZE=512;
  char buf[BUFSIZE+1]; 
  buf[BUFSIZE+1] = '\0';
  int numeroLetti;
  boolean procAttivo=true;
  pNodoListaThread nodo=tp->listaFigli->inizio;
  int numeroScrittiFiglio;
  fprintf(tp->file,"Mi metto in ascolto dei thread attivi\n");
  while(nodo!=NULL){
      if(nodo->thread->state==RUN){
          fprintf(tp->file,"Ascolto la pipe del thread con comando %s e pid %d\n",
                  nodo->thread->command,nodo->thread->pid);
          //Leggo dalla pipe
          numeroLetti=read(nodo->thread->pipe[0], &buf, BUFSIZE);
          fprintf(tp->file,"Ho letto %d byte dal thread\n",numeroLetti);
          procAttivo=false;
          switch(numeroLetti){
              case -1: //Errore
                      fprintf(tp->file,"Errore durante la lettura della pipe del processo identificato dal comando: %s\n",nodo->thread->command);
                      procAttivo=false;
                      break;
              case 0: //Pipe chiusa il figli e' terminato
                      fprintf(tp->file,"Il processo identificato dal comando: %s e' terminato\n",
                              nodo->thread->command);
                      procAttivo=false;
                      break;
              default ://Scrivo sul log del figlio                    
                      if(numeroLetti<BUFSIZE){
                          buf[numeroLetti]='\0';
                      }else{
                          buf[BUFSIZE+1]='\0';
                      }
                      numeroScrittiFiglio=write(nodo->thread->file,&buf,numeroLetti);
                      if(numeroScrittiFiglio<0){
                          fprintf(tp->file,"Errore durante la lettura della pipe del processo identificato dal comando: %s\n",nodo->thread->command);
                          procAttivo=false;
                      }else{
                          procAttivo=true;
                          fprintf(tp->file,"Ho scritto %d byte dell'output del comando: %s\n",
                              numeroScrittiFiglio,nodo->thread->command);
                      }                    
          }
          if(procAttivo==false){
            nodo->thread->state=FINISH;
          }
      }
      nodo=nodo->next;
  }
}

/*
 * avviaFiglioCasualmente:
 * Preleva un byte da /dev/random e usa questo byte per scegliere il processo da avviare
 * Ritorna -1 se non ha avviato nessuno
*/
int ThreadPadre_avviaFiglioCasualmente (pThreadPadre tp){
  /*Leggi un carattere e avvia il figlio in modulo nella lista, se e' gia avviato non far nulla*/
    int avviato=-1;
    char* path="/dev/random";
    int fd=open(path, O_RDONLY);
    if(fd==-1){
        perror("/dev/random");
    }
    char buf[1];
    int letti=read(fd,buf,1);
    if(letti<1){
        perror("/dev/random impossibile leggere");
    }else{
      int casuale=buf[0];
      if(casuale<0){
          casuale=(-1)*casuale;
      }
      fprintf(tp->file,"La lista ha dimensione: %d\n",tp->listaFigli->dim);
      fprintf(tp->file,"Ho letto da /dev/random: %d\n",casuale);
      int numeroDaAvviare;
      numeroDaAvviare=casuale%tp->listaFigli->dim;
      fprintf(tp->file,"Ho da avviare il thread numero: %d\n",numeroDaAvviare);
      avviato=ThreadPadre_startFiglio(tp,numeroDaAvviare);
    }
    if(close(fd)==-1){
        perror("Impossibile chiudere /dev/random");
    }
    return avviato;
}

/*
 * newFiglio
 * Genera un nuovo figlio assegnato al comando
*/
void ThreadPadre_newFiglio(pThreadPadre tp, char * comando){
    int pid;
    pThreadFiglio thread=NULL;
    thread=ThreadFiglio_create(comando);
    if((pid=(fork()))==0){
      //Codice figlio
      ThreadFiglio_closePipeRead(thread);
      dup2(thread->pipe[1], STDOUT_FILENO);
      system(comando);
      //La variabile non e' condivisa quindi chiudo tutto.
      ThreadFiglio_setFinish(thread);
      ThreadFiglio_destroy(thread);
      exit(0);
    }else{
      //Codice padre
        kill(pid,SIGSTOP); 
        ThreadFiglio_setPid(thread,pid);
        ThreadFiglio_closePipeWrite(thread);
        fprintf(tp->file,"Ho creato il processo con comando: %s e pid: %d\n",comando,pid);
        fprintf(tp->file,"Sto inserendo il processo con comando: %s\n",comando);
        ListaThread_insert(tp->listaFigli,thread);
        fprintf(tp->file,"Ho inserito il processo con comando: %s nella lista dei processi\n",comando);
    }
}

/*
 * creaFileDiLog
 * Creo il file di log del figlio
 */
void creaFileDiLog(pThreadFiglio thread){
          /*
           * Creo il file di log del padre
           */
          // Ottengo il timestamp
          time_t t = time(NULL);
          struct tm tm = *localtime(&t);  
          char indirizzo[29+strlen(thread->command)+strlen(LOGDIRECTORY)];
           //or.mi.se.gi.me.an.out
          int n=sprintf(indirizzo,LOGDIRECTORY"/cmd.%s.%d.%d.%d.%d.%d.%d.out",
                             thread->command,
                             tm.tm_hour, tm.tm_min, tm.tm_sec,
                             tm.tm_mday,tm.tm_mon + 1,tm.tm_year + 1900);
          thread->file=open(indirizzo, O_WRONLY | O_CREAT | O_TRUNC);
          if(thread->file==-1){
              perror(strcat("Errore creazione file: ",indirizzo));
          }else{
              //Assegno da subito i privilegi per il file
              chmod(indirizzo,O_RDWR | S_IRWXU |S_IRWXG |S_IRWXO);
              thread->fileAperto=true;
          } 
}

/*
 * Avvia Figlio
 * Avvia un figlio numero figlio
 * Ritorna : 0 figlio avviato, altrimenti -1
*/
int ThreadPadre_startFiglio(pThreadPadre tp, int numeroFiglio){
    pThreadFiglio thread=ListaThread_find(tp->listaFigli,numeroFiglio);
    fprintf(tp->file,"Ho da avviare il thread identificato dal comando %s e con pid %d\n",
                thread->command,thread->pid );
    State st=ThreadFiglio_getState(thread);
    if(st==RUN){
        fprintf(tp->file,"Il thread identificato dal comando %s e con pid %d e' gia' avviato\n",
                thread->command,thread->pid );
        return -1;
    }else{
      st=ThreadFiglio_start(thread);
      switch(st){
          case RUN: 
                    creaFileDiLog(thread);
                    tp->listaFigli->attivi++;
                    fprintf(tp->file,"Ho avviato il thread identificato dal comando: %s con pid: %d\n",
                    thread->command,thread->pid);
                    break;
          case SLEEP:fprintf(tp->file,"Impossibile avviare il thread identificato dal comando: %s con pid: %d\n",
                    thread->command,thread->pid);
                    break; 
          case FINISH:fprintf(tp->file,"Il thread identificato dal comando: %s con pid: %d e' gia' terminato\n",
                    thread->command,thread->pid);
                    break; 
          default : fprintf(tp->file,"Lo stato del thread identificato dal comando %s con pid %d non e' valido\n",
                  thread->command,thread->pid);                    
      }
      if(st==RUN){
          return 0 ;
      }else{
          return -1;
      }
    }
}

/*
 * distruttore:
 * Termina il thread e i suoi figli
 * return: un intero, se 0  distruzione avvenuta
                      se -1 distruzione non avvenuta 
*/
int ThreadPadre_destroy(pThreadPadre tp){ 
    ListaThread_destroy(tp->listaFigli);
    fclose(tp->file);
}



